﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.Reflection.Emit;

namespace IlGenUtil
{
	public interface DMethod<Args, Ret>
	{
		IlGen<Nil, Args, Ret> IlGen { get; }
		DynamicMethod DynamicMethod { get; }
	}


	public static class Method
	{
		private struct _DM<Args, Ret> : DMethod<Args, Ret>
		{
			public IlGen<Nil, Args, Ret> IlGen
			{
				get { return ilGen; }
			}
			private IlGen<Nil, Args, Ret> ilGen;

			public DynamicMethod DynamicMethod
			{
				get { return this.method; }
			}
			private DynamicMethod method;

			public _DM(String name, Type retType, Type[] paramTypes)
			{
				this.method = new DynamicMethod(name, retType, paramTypes);
				this.ilGen = new IlGen<Nil, Args, Ret>(this.method.GetILGenerator());
			}
		}


		public static DMethod<Nil, Nil> New(String name)
		{
			return new _DM<Nil, Nil>(name, typeof(void), new Type[] { });
		}

		public static DMethod<Nil, Ret> New<Ret>(String name)
		{
			return new _DM<Nil, Ret>(name, typeof(Ret), new Type[] { });
		}

		public static DMethod<Lst<A, Nil>, Ret> New<A, Ret>(String name)
		{
			return new _DM<Lst<A, Nil>, Ret>(name, typeof(Ret), new Type[] { typeof(A) });
		}

		public static DMethod<Lst<A, Lst<B, Nil>>, Ret> New<A, B, Ret>(String name)
		{
			return new _DM<Lst<A, Lst<B, Nil>>, Ret>(name, typeof(Ret), new Type[] { typeof(A), typeof(B) });
		}
		public static DMethod<Lst<A, Lst<B, Lst<C, Nil>>>, Ret> New<A, B, C, Ret>(String name)
		{
			return new _DM<Lst<A, Lst<B, Lst<C, Nil>>>, Ret>(name, typeof(Ret), new Type[] { typeof(A), typeof(B), typeof(C) });
		}

	}


	public static class MethodExtensions
	{
		public static void Invoke(this DMethod<Nil, Nil> m)
		{
			m.DynamicMethod.Invoke(null, null);
		}

		public static Ret Invoke<Ret>(this DMethod<Nil, Ret> m)
		{
			return (Ret)m.DynamicMethod.Invoke(null, null);
		}

		public static Ret Invoke<A, Ret>(this DMethod<Lst<A, Nil>, Ret> m, A a)
		{
			return (Ret)m.DynamicMethod.Invoke(null, new object[] { a });
		}

		public static Ret Invoke<A, B, Ret>(this DMethod<Lst<A, Lst<B, Nil>>, Ret> m, A a, B b)
		{
			return (Ret)m.DynamicMethod.Invoke(null, new object[] { a, b });
		}
	}
}
